import requests
import json
import pandas as pd
from datetime import datetime
import openpyxl
from openpyxl.styles import Alignment, Border, Side, Font, PatternFill
from openpyxl.utils import get_column_letter
import sys
import os

from get_config import get_token
from sun_tool_fee import get_sun_bill


class VoucherDataProcessor:
    """
    凭证数据处理器：从API获取数据并转换为Excel格式
    """

    def __init__(self):
        self.headers = {}

    def generate_request_params(self, start_year=1970, start_period=1,
                                end_year=None, end_period=12,
                                page_size=1000000, current_page=1,
                                **kwargs):
        """
        自动生成请求参数

        :param start_year: 开始年份，默认1970
        :param start_period: 开始期间，默认1
        :param end_year: 结束年份，默认当前年份
        :param end_period: 结束期间，默认12
        :param page_size: 每页大小，默认1000000
        :param current_page: 当前页，默认1
        :param kwargs: 其他可选参数
        :return: 请求参数字典
        """
        # 获取当前日期
        now = datetime.now()

        # 如果没有指定结束年份，使用当前年份
        if end_year is None:
            end_year = now.year

        # 生成displayDate (格式: YYYY-MM)
        display_date = kwargs.get('displayDate', now.strftime('%Y-%m'))

        # 构建请求参数
        request_params = {
            "beizhu": kwargs.get('beizhu'),
            "voucherState": kwargs.get('voucherState'),
            "summary": kwargs.get('summary'),
            "endCode": kwargs.get('endCode'),
            "simpleCondition": kwargs.get('simpleCondition'),
            "creator": kwargs.get('creator'),
            "currencyId": kwargs.get('currencyId'),
            "startCode": kwargs.get('startCode'),
            "displayDate": display_date,
            "endAmount": kwargs.get('endAmount'),
            "startAmount": kwargs.get('startAmount'),
            "startYear": start_year,
            "startPeriod": start_period,
            "endYear": end_year,
            "endPeriod": end_period,
            "page": {
                "pageSize": page_size,
                "currentPage": current_page
            },
            "userOrderField": kwargs.get('userOrderField'),
            "order": kwargs.get('order'),
            "accountIds": kwargs.get('accountIds', []),
            "hasNote": kwargs.get('hasNote', False)
        }

        return request_params

    def fetch_data(self, api_url, request_params=None, token=None,
                   auto_params=False, **kwargs):
        """
        从API获取数据

        :param api_url: API接口地址
        :param request_params: 请求参数（字典格式），如果为None且auto_params=True，则自动生成
        :param token: 认证token
        :param auto_params: 是否自动生成请求参数
        :param kwargs: 用于自动生成参数的额外参数
        :return: API返回的数据
        """
        try:
            # 如果需要自动生成参数
            if auto_params and request_params is None:
                request_params = self.generate_request_params(**kwargs)
                print("自动生成的请求参数:")
                print(json.dumps(request_params, indent=2, ensure_ascii=False))

            # 设置请求头
            headers = {
                'Content-Type': 'application/json',
            }
            if token:
                headers['Token'] = token


            print(f"正在请求API: {api_url}")
            print(f"请求参数大小: pageSize={request_params.get('page', {}).get('pageSize', 'N/A')}")

            # 发送请求
            response = requests.post(
                api_url,
                headers=headers,
                json=request_params,
                timeout=60  # 增加超时时间，因为可能请求大量数据
            )

            # 检查响应状态
            response.raise_for_status()

            # 返回JSON数据
            data = response.json()
            print(f"成功获取数据，响应状态: {response.status_code}")

            return data

        except requests.exceptions.Timeout:
            print("请求超时，请检查网络连接或减少请求数据量")
            return None
        except requests.exceptions.RequestException as e:
            print(f"请求API时出错: {e}")
            if hasattr(e, 'response') and e.response is not None:
                print(f"响应状态码: {e.response.status_code}")
                print(f"响应内容: {e.response.text[:500]}")  # 只打印前500个字符
            return None
        except json.JSONDecodeError as e:
            print(f"解析JSON数据时出错: {e}")
            return None

    def parse_voucher_data(self, json_data):
        """
        解析凭证数据，转换为Excel所需的格式

        :param json_data: API返回的JSON数据
        :return: 格式化后的数据列表
        """
        excel_data = []

        try:
            # 检查数据结构
            if not json_data.get('result'):
                print(f"API返回错误: result={json_data.get('result')}")
                if 'message' in json_data:
                    print(f"错误信息: {json_data.get('message')}")
                return excel_data

            if not json_data.get('value'):
                print("返回数据中没有value字段")
                return excel_data

            doc_list = json_data['value'].get('docList', [])

            if not doc_list:
                print("没有找到凭证数据")
                return excel_data

            print(f"找到 {len(doc_list)} 张凭证")

            # 按日期对凭证排序
            doc_list.sort(key=lambda x: x.get('voucherDate', ''))

            for idx, doc in enumerate(doc_list, 1):
                # 获取凭证基本信息
                voucher_date = doc.get('voucherDate', '')
                doc_type_and_code = doc.get('docTypeAndCode', '')
                attached_num = doc.get('attachedNum', 0)
                creator = doc.get('creator', '')
                auditor = doc.get('auditor', '')
                voucher_state = doc.get('voucherStateName', '')

                # 获取分录信息
                entries = doc.get('entrys', [])

                if not entries:
                    print(f"凭证 {doc_type_and_code} 没有分录")
                    continue

                # 记录第一个分录的位置，用于合并单元格
                first_entry_index = len(excel_data)

                # 处理每个分录
                for i, entry in enumerate(entries):
                    # 只在第一个分录显示日期、凭证号和附单据数
                    if i == 0:
                        row_data = {
                            '日期': voucher_date,
                            '凭证字号': doc_type_and_code,
                            '摘要': entry.get('summary', ''),
                            '会计科目': entry.get('accountCodeName', ''),
                            '借方金额': entry.get('amountDr', 0) or '',
                            '贷方金额': entry.get('amountCr', 0) or '',
                            '附单据数': attached_num if attached_num > 0 else '',
                            '制单人': creator,
                            '审核人': auditor
                        }
                    else:
                        row_data = {
                            '日期': '',  # 后续分录不显示日期
                            '凭证字号': '',  # 后续分录不显示凭证号
                            '摘要': entry.get('summary', ''),
                            '会计科目': entry.get('accountCodeName', ''),
                            '借方金额': entry.get('amountDr', 0) or '',
                            '贷方金额': entry.get('amountCr', 0) or '',
                            '附单据数': '',  # 后续分录不显示附单据数
                            '制单人': '',
                            '审核人': ''
                        }
                    excel_data.append(row_data)

                # 添加合计行
                total_dr = sum((e.get('amountDr', 0) or 0) for e in entries)
                total_cr = sum((e.get('amountCr', 0) or 0) for e in entries)

                total_row = {
                    '日期': '',
                    '凭证字号': '合计',
                    '摘要': '',
                    '会计科目': '',
                    '借方金额': f'{total_dr:,.2f}' if total_dr > 0 else '',
                    '贷方金额': f'{total_cr:,.2f}' if total_cr > 0 else '',
                    '附单据数': '',
                    '制单人': '',
                    '审核人': ''
                }
                excel_data.append(total_row)

                # 添加空行分隔不同凭证（除了最后一个凭证）
                if idx < len(doc_list):
                    empty_row = {col: '' for col in ['日期', '凭证字号', '摘要', '会计科目',
                                                     '借方金额', '贷方金额', '附单据数',
                                                     '制单人', '审核人']}
                    excel_data.append(empty_row)

            print(f"成功解析 {len(doc_list)} 张凭证，共 {len(excel_data)} 行数据")

        except Exception as e:
            print(f"解析数据时出错: {e}")
            import traceback
            traceback.print_exc()

        return excel_data

    def save_to_excel(self, data, output_file='凭证数据.xlsx'):
        """
        将数据保存为Excel文件，按照标准会计凭证格式

        :param data: 格式化后的数据
        :param output_file: 输出文件名
        """
        try:
            if not data:
                print("没有数据可以保存")
                return False

            # 创建DataFrame
            df = pd.DataFrame(data)

            # 确保金额列为数值类型
            for col in ['借方金额', '贷方金额']:
                df[col] = pd.to_numeric(df[col].astype(str).str.replace(',', ''), errors='coerce').fillna('')

            # 保存到Excel
            with pd.ExcelWriter(output_file, engine='openpyxl') as writer:
                df.to_excel(writer, sheet_name='凭证明细', index=False)

                # 获取workbook和worksheet对象
                workbook = writer.book
                worksheet = writer.sheets['凭证明细']

                # 设置列宽
                column_widths = {
                    'A': 12,  # 日期
                    'B': 15,  # 凭证字号
                    'C': 30,  # 摘要
                    'D': 35,  # 会计科目
                    'E': 15,  # 借方金额
                    'F': 15,  # 贷方金额
                    'G': 10,  # 附单据数
                    'H': 10,  # 制单人
                    'I': 10,  # 审核人
                }

                for col, width in column_widths.items():
                    worksheet.column_dimensions[col].width = width

                # 设置边框样式
                thin_border = Border(
                    left=Side(style='thin'),
                    right=Side(style='thin'),
                    top=Side(style='thin'),
                    bottom=Side(style='thin')
                )

                # 设置表头样式
                header_font = Font(bold=True, size=11)
                header_fill = PatternFill(start_color="E0E0E0", end_color="E0E0E0", fill_type="solid")

                for cell in worksheet[1]:
                    cell.font = header_font
                    cell.alignment = Alignment(horizontal='center', vertical='center')
                    cell.border = thin_border
                    cell.fill = header_fill

                # 设置数据区域的样式
                for row_num in range(2, worksheet.max_row + 1):
                    row = worksheet[row_num]

                    # 检查是否是合计行
                    is_total_row = worksheet[f'B{row_num}'].value == '合计'

                    for cell in row:
                        cell.border = thin_border

                        # 合计行加粗
                        if is_total_row:
                            cell.font = Font(bold=True)
                            cell.fill = PatternFill(start_color="F0F0F0", end_color="F0F0F0", fill_type="solid")

                        # 金额列右对齐
                        if cell.column in [5, 6]:  # E和F列
                            cell.alignment = Alignment(horizontal='right', vertical='center')
                            # 格式化金额
                            if cell.value and isinstance(cell.value, (int, float)):
                                cell.number_format = '#,##0.00'
                        # 日期和凭证字号居中
                        elif cell.column in [1, 2, 7]:  # A, B, G列
                            cell.alignment = Alignment(horizontal='center', vertical='center')
                        else:
                            cell.alignment = Alignment(horizontal='left', vertical='center')

                # 设置打印选项
                worksheet.print_options.horizontalCentered = True
                worksheet.print_options.verticalCentered = False
                worksheet.page_margins.left = 0.7
                worksheet.page_margins.right = 0.7
                worksheet.page_margins.top = 0.75
                worksheet.page_margins.bottom = 0.75
                worksheet.page_margins.header = 0.3
                worksheet.page_margins.footer = 0.3

                # 设置页面方向为横向
                worksheet.page_setup.orientation = worksheet.ORIENTATION_LANDSCAPE
                worksheet.page_setup.paperSize = worksheet.PAPERSIZE_A4
                worksheet.page_setup.fitToWidth = 1
                worksheet.page_setup.fitToHeight = False

            # 获取文件大小
            file_size = os.path.getsize(output_file)
            file_size_mb = file_size / (1024 * 1024)

            print(f"数据已成功保存到: {output_file}")
            print(f"文件大小: {file_size_mb:.2f} MB")
            print(
                f"包含 {len([d for d in data if d.get('凭证字号') and '合计' not in str(d['凭证字号']) and d['凭证字号'] != ''])} 张凭证")

            return True

        except Exception as e:
            print(f"保存Excel文件时出错: {e}")
            import traceback
            traceback.print_exc()
            return False

    def process(self, api_url, request_params=None, token=None,
                output_file=None, auto_params=False, **kwargs):
        """
        完整的处理流程：获取数据 -> 解析 -> 保存为Excel

        :param api_url: API接口地址
        :param request_params: 请求参数，如果为None且auto_params=True，则自动生成
        :param token: 认证token
        :param output_file: 输出文件名
        :param auto_params: 是否自动生成请求参数
        :param kwargs: 用于自动生成参数的额外参数
        """
        print("=" * 50)
        print("开始处理凭证数据...")
        print("=" * 50)

        # 如果没有指定输出文件名，自动生成
        if output_file is None:
            output_file = f"凭证数据_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"

        # 1. 获取数据
        print("\n步骤 1/3: 正在获取数据...")
        json_data = self.fetch_data(api_url, request_params, token, auto_params, **kwargs)

        if not json_data:
            print("❌ 获取数据失败")
            return False

        # 2. 解析数据
        print("\n步骤 2/3: 正在解析数据...")
        excel_data = self.parse_voucher_data(json_data)

        if not excel_data:
            print("❌ 数据解析失败或无有效数据")
            return False

        # 3. 保存为Excel
        print(f"\n步骤 3/3: 正在保存到Excel...")
        success = self.save_to_excel(excel_data, output_file)

        if success:
            print("\n" + "=" * 50)
            print("✅ 处理完成！")
            print("=" * 50)
        else:
            print("\n❌ 保存失败")

        return success


# 使用示例
def main():
    """
    主函数：演示如何使用VoucherDataProcessor
    """

    # 创建处理器实例
    processor = VoucherDataProcessor()

    # === 方式1：使用自动生成的请求参数 ===
    print("\n使用自动生成的请求参数:")

    # 配置API信息
    api_url = "https://your-api-domain.com/api/voucher/list"  # 替换为实际的API地址
    token = "your-token.txt-here"  # 替换为实际的token

    # 自动生成请求参数并处理
    processor.process(
        api_url=api_url,
        token=token,
        output_file="凭证数据_自动.xlsx",
        auto_params=True,  # 使用自动参数
        start_year=1970,
        start_period=1,
        # end_year 会自动设置为当前年份
        end_period=12,
        page_size=1000000  # 获取所有数据
    )

    # # === 方式2：手动指定请求参数 ===
    # print("\n使用手动指定的请求参数:")
    #
    # # 手动构建请求参数（根据您提供的格式）
    # manual_params = {
    #     "beizhu": None,
    #     "voucherState": None,
    #     "summary": None,
    #     "endCode": None,
    #     "simpleCondition": None,
    #     "creator": None,
    #     "currencyId": None,
    #     "startCode": None,
    #     "displayDate": datetime.now().strftime('%Y-%m'),  # 当前年月
    #     "endAmount": None,
    #     "startAmount": None,
    #     "startYear": 1970,
    #     "startPeriod": 1,
    #     "endYear": datetime.now().year,  # 当前年份
    #     "endPeriod": 12,
    #     "page": {
    #         "pageSize": 1000000,  # 获取所有数据
    #         "currentPage": 1
    #     },
    #     "userOrderField": None,
    #     "order": None,
    #     "accountIds": [],
    #     "hasNote": False
    # }
    #
    # # 处理数据
    # processor.process(
    #     api_url=api_url,
    #     request_params=manual_params,
    #     token.txt=token.txt,
    #     output_file=f"凭证数据_{datetime.now().strftime('%Y%m')}.xlsx"
    # )


# 便捷函数：一键处理所有数据
def quick_process_all(api_url, token=None, output_file=None,
                      start_year=1970, end_year=None):
    """
    便捷函数：快速处理所有凭证数据

    使用方法：
    quick_process_all(
        api_url="https://your-api.com/vouchers",
        token.txt="your-token.txt"
    )
    """
    processor = VoucherDataProcessor()
    return processor.process(
        api_url=api_url,
        token=token,
        output_file=output_file,
        auto_params=True,
        start_year=start_year,
        end_year=end_year,
        page_size=1000000  # 获取所有数据
    )


# 便捷函数：处理特定期间的数据
def process_period(api_url, token=None, year=None, start_period=1,
                   end_period=12, output_file=None):
    """
    处理特定年度期间的凭证数据

    使用方法：
    process_period(
        api_url="https://your-api.com/vouchers",
        token.txt="your-token.txt",
        year=2024,
        start_period=1,
        end_period=6  # 获取2024年1-6月的数据
    )
    """
    if year is None:
        year = datetime.now().year

    if output_file is None:
        output_file = f"凭证数据_{year}年{start_period}-{end_period}期.xlsx"

    processor = VoucherDataProcessor()

    request_params = processor.generate_request_params(
        start_year=year,
        start_period=start_period,
        end_year=year,
        end_period=end_period,
        page_size=1000000
    )

    return processor.process(
        api_url=api_url,
        request_params=request_params,
        token=token,
        output_file=output_file
    )


if __name__ == "__main__":
    # 运行主函数
    # main()

    # 或者使用便捷函数
    # quick_process_all(
    #     api_url="https://jcdz.jcszfw.com/v1/cw/pzgl/query",
    #     token=get_token() # 获取Token
    # )
    # get_sun_bill("2024-09-30")
    pass